Math operators found at https://rpruim.github.io/s341/S19/from-class/MathinRmd.html


?Matrix
library(Matrix)
library(tidymodels)
library(knitr)
library(dplyr)
library(tidyverse)
library(tidyr)
library(ggplot2)
library(readr)
library(caret)
library(e1071)
library(caTools)
library(rsample)

Lecture 3 PH + in class

In class assignment

Problem 1.1/1.2

boston <- read.delim("boston_corrected.txt", skip = 9, header = TRUE)
dim(boston)
[1] 506  21
sum(is.na(boston))
[1] 0

problem 3

%>% is a piping function. Rather than specifying the variable i want to target for all operations I have already prompted the updated variable. Syntax boston <- select(boston,-“OBS.”,-“TOWN”,-“TOWN.”,-“TRACT”,-“LON”,-“LAT”,-“MEDV”) would give same result. Piping is useful when you wish to to additional variable updates in a row.

boston <- boston %>%
  select(-"OBS.",-"TOWN",-"TOWN.",-"TRACT",-"LON",-"LAT",-"MEDV")

problem 4

remember to save variable change to the updated variable using <-

Be aware that in this example the logic is:

Names(boston) - the columns of df boston -, needs to be set to lower. We need to both save the lowercase names to our dataframe and specify we want them lower, as such names(boston)) is called twice.

names(boston) <- tolower(names(boston))

problem 5

boston <- rename(boston,medv=cmedv)
head(boston)

problem 6

I’m using the readr write_csv functionality. It’s smart and knows what to do regarding headers etc already. If you were to use base R I would do the function as follows

write.csv(boston, file = “BostonBI.csv”, na = “NA”) specifying of additional parameters

write_csv(boston, "BostonBI.csv")

Problem 2.2

boston1 <- boston %>%
  select(tax, medv)
  summary(boston1)
      tax             medv      
 Min.   :187.0   Min.   : 5.00  
 1st Qu.:279.0   1st Qu.:17.02  
 Median :330.0   Median :21.20  
 Mean   :408.2   Mean   :22.53  
 3rd Qu.:666.0   3rd Qu.:25.00  
 Max.   :711.0   Max.   :50.00  
  cor(boston1)
            tax       medv
tax   1.0000000 -0.4719788
medv -0.4719788  1.0000000

Problem 3

ggplot(boston1, aes(x=tax)) + geom_density() + labs(x="US$")

Problem 4

ggplot(boston1, aes(x=tax)) + geom_histogram(binwidth = 5)

problem 5

Need to attach the dataframe in order to do the factorial operations. Could probably be solved by piping? Will add to later.

First we define our string value factorial, taxlabel. Then we define the value parameters assigned to the string. If tax is less than 300, the value assigned is low, or 0. If between 300 and 600 value assigned is medium, or 1. If above 600 value assigned is high, or 3.

The factor(tax_discrete,0:2,taxlabel) defines the range spread for string values of the factorial.

attach(boston)
taxlabel <- c("low","medium","high")
tax_discrete <- 0 + (tax > 300) + (tax < 600)
tax2 <- factor(tax_discrete,0:2,taxlabel)

boston <- cbind(boston,tax2)

head(boston)
NA
NA

Problem 6

Coord_flip flips the x and y values. Used here for increased readability.

ggplot(boston, aes(x=tax,y=medv)) + geom_boxplot() + coord_flip()

###problem 7 ggplot syntax = aes = Aesthetic (udseende) geom = type of chart, point = scatter, boxplot = boxplot, histogram = histo. stat_smooth is a best fit model, you can define method of the fit by (method=“x”), here “lm” is linear model fit

ggplot(boston, aes(x=tax,y=medv)) +
  geom_point() + 
  ylim(0,50) + 
  # stat_smooth() 
  stat_smooth(method="lm")

NA
NA

Lecture 4

Predictive learnings

Input = independent variables (IV) = features = predictors = X

\[x_1,…x_n\]

Output = Dependent variables (DV) = response = Y

\[Y_1,…,y_m\]

other var. that affect Y, but those values are neither observed nor controlled (noise?)

\[Z_1,…Z_k\]

Matemathical model

\[ (1) y_k = g_k(x1,..., x_n, z_1, z_L), =k = 1,K \]

\(y_k\) can be any row in our dataset

Statistical model

\[ (2) y_k = f_k(x_1, .... , x_n) + e_k, k = 1,K \]

\(f_k\) = Function of the observed inputs \[ f_k \]

\(e_k\) = an additional random stochastic component / error term \[ e_k \]

If denoting \[X = (x_1,x_2,...,x_p)\] then (2) becomes \[y_k = ƒ(X) + e_k\] even if we find the perfect approximation of \(ƒ(X)\) we will never be able to compute for the random factor \(e_k\)

\(f\) Is used as an estimation for new y observations, which helps us understand the mechanism that is produced by the data (y) output to help intervene in the future

Example 1(slide 14) (ISL p. 16-17) is an Ordinary least squares (OLSS) regression Example 2(slide 15) (ISL, p. 16-17) OLS estimation can be viewed as a projection onto the linear space spanned by the regressors.

For predictions: - Focus on reducible errors

if: \[ E(Y-Ŷ)^2 = E[f(X)+e-f̂(X)]^2\] Then \[ E(Y-Ŷ)^2 = [f(X)-f̂(X)]^2+Var(e)\]

Where \([f(X)-f̂(X)]^2\) is reducible and \(Var(e)\)is irreducible

For inference put focus on - Which predictors \(X\) associate with response \(y\) - Magnitude & direction - relationship (Linear or other) - interaction effects

In ML we’re mainly focused on the predictive perspective rather than the interference - It is however possible in some situations to focus on both aspects at once

ESTIMATION OF ƒ

  1. Parametric
  2. Non-parametric

1. Parametric

’a priori assumption” : Relating to ro denoting reasoning or knowledge which proceeds from theoretical deduction rather than observation or experience, ie. “sexuality may be a factor but it cannot be assumed a priori”

We fit the model based on our a priori assumptions. If we expect linear fit we estimate the parameters beta (Multiple linear regression. If you use ≈ you don’t note the error variable \(e\) as it’s an estimation)

\[ y ≈ ß_o+ß_1 X_1+ß_2 X_2+…+ß_p X_p \] where \(ß_0,ß_1,ß_2\) and \(ß_p\) are our estimators

For example 1 slide 23 \(income ≈ ß_0+ß_1YoE_1 +ß_2Seniority_2\)

for example 2 slide 24 (Polynominal and interaction included),(Followup after class, incorrect) \(PI ≈ ß_o+ß_1 X_1+ß_2 X_2+…+ß_p X_p\). The interaction changes the curvature of the 2-dimensional plane.

2. Non-parametric

No assumption; \(ƒ\) is very flexible

Advantages: Predictive accuracy Disadvantages: Large number og observations is required; overfitting risk; low interpretability. Non a priori

example from class: Crumbled up paper. Imagine we have to predict the plane of the paper, it’s incredibly difficult as the paper is all crambled up. Sometimes in non-parametric \(ƒ\) can be considered a blackbox as it’s borderline impossible to approximate.

will be covered in Machine Learning 2

method suitability

include_graphics("flexibilty vs interp.png")


Data partitioning

Split datasets into partitions, one for training and one for testing Example: From Tutoral 1 we know that (dataset not included)

# library(rsample)
# set.seed(123) 
# split_1  <- initial_split(df, prop = 0.6)
# train <- training(split_1)
# test <- testing(split_1)

For above; Load rsample library for the utilties

Seed for reproductibility - The seed specifies the point at which we would like to split the dataset. Without a seed a random number will be assigned (number assigned is not actually random but rather pseudorandom)

define variable of split = split_1, where df is our dataframe or tabel.

Prop defines where the dataset is split, 0.6 = 60/40, 0.5 would equal 50/50, etc.

training and testing are functions of the rsample library. using these functions with our split variable will automatically assign the desired split

Approaches for partitioning¨

splits will typically be done in an 80:20 fashion (prop = 0.8)

  1. Random split
  • Tradtional technique, simplest way. Used in the book.
  1. Stratified split
  • Considers target variable(\(y\)) and will try to group for it before split - if dataset has “1” and “0” varlues and you pick randomly it can split the sets poorly, here grouping them beforehand ensures data integrity for both sets

expand with knowledge from https://bradleyboehmke.github.io/HOML/process.html

Re-sampling

include_graphics("ML process.png")

  • Single training data leads to inaccurate results. To avoid this we utilize re-sampliung methods

    • note from lecture 4: Data pre-process/feature engineering happens before you do your data splits. If you want to impact your data in a specific way after splitting to test different results you can do it after establishing split. In practice it’s done between every step including modelling. doing the pre-processing later is more optimal as you get furhter insigt into your data.

    if done at step 1 we average the dataset immediately. We want a pure untouched dataset without influence frmo our actions.

    if done at step 2 we standardize the mean for the whole dataset. The same mean will be implemented in both the analyzis of the model and the evaluation. Doing it this way is good

    If done at step 3 we can do the integrations between each fold of the model. This increases the accuracy between each fold for the next parse. This takes a load of time due to multiple optimizations.

    • data leakage: in some way the process of modelling we affect the unseen observations with information from the training observations. Can skew dataset and impact your error.

k-fold cross validation

We can force the training sets to be stratified throughout the k-fold cross validation.

include_graphics("k-fold method.png")

bootstrapping

extracting of observations with replacement. out of this dataset you can extract samples, put them back, same observation can be extracted multiple times. Phillip will touch on this later.

include_graphics("Bootstrapping.png")

Use multiple split when practicing to get best results

Knowing this, the standard procedure for model building should be

include_graphics("Procedure modelling.png")

Model evaluation criteria

###will be expanded upon

expand with knowledge from https://bradleyboehmke.github.io/HOML/process.html

Regression models:

bold = most common

  • MSE (mean squared error)

  • RMSE (root mean squared error)

  • Deviance

  • MAE

  • R-squared

Classification models

  • Misclassification rate

  • Mean per class error

  • MSE

  • Cross-entropy

  • Gini index

  • Confusion matric

  • Accuracy, Precision, Sensitivity/Recall, Specificity

  • ROC and AUC*

Errors

Training errors(MSE) \[MSE = \frac{1}{n} \sum_{i = 1}^{n} (y_i - f̂(x_i))^2 \] Training error(RMSE) \[\sqrt MSE\]

Testing error (test MSE) \[Ave(y_o-f̂(x_o))^2\] where \(x_o,y_o\) are obs. not used to train. Otherwise formulas are pretty much the same

overfitting

Small train error

High test error

Our model(algorithm) is trying too hard to find a suited fit

Variance of fit = Amount by which \(f̂\) would change if estimated using different training set

bias of fit = error introduced by approximating real-life problem in simple models

aim to minimize both

\[E(y_0-f̂(x_o))^2 = Var(f̂(x_o)) + [bias(f̂(x_o))]^2 + Var(e)\]

where

\(E(y_0-f̂(x_o))\) = Expected test MSE

\(Var(f̂(x_o\) = Variance of fit

\([bias(f̂(x_o))]\) = Bias of fit

\(Var(e)\) = Irreducible error

include_graphics("Method choice example.png")

On the example we have three models fitted. Dataset was simulated based on black (true \(ƒ\)).Smoothing spines on left graph is too aggressively trying to fit datapoints.

For the linear regression the training error is the lower yellow square, testing errorr upper yellow square. These are the errors when we run a LM on the dataset. For blue and green we see same relationship. As shown here a higher flexibility model is not laways the optimal choice. In the example we would go for the blue MSE to avoid overfitting.

We always aim for lowest test error.

Lecture conclusions

Increase in method flexibility (more advanced methods, NN), we can reduce the prediction error (bias). Increasing flexibility does however have diminishing returns and will eventually increase our variance further than reducing our bias.

Machine learning has to one-size-fits-all model, we must utilize all tools and models available to us to treat each dataset independently.

Lecture 4 coding 🤓

Sample formula interfaces


ames <- AmesHousing::make_ames()
# Sale price as function of neighborhood and year old

lm_lm <- lm(Sale_Price ~Neighborhood + Year_Sold, data = ames)

#lm is used to fit linear models

lm_glm <- glm(Sale_Price ~Neighborhood + Year_Sold, data = ames, family = gaussian)

#glm is used to fit generalized linear models

lm_caret <- train(Sale_Price ~Neighborhood + Year_Sold, data = ames, method = "lm")
Warning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleadingWarning: prediction from a rank-deficient fit may be misleading
lm_caret
Linear Regression 

2930 samples
   2 predictor

No pre-processing
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 2930, 2930, 2930, 2930, 2930, 2930, ... 
Resampling results:

  RMSE      Rsquared   MAE     
  53433.06  0.5638988  36020.18

Tuning parameter 'intercept' was held constant at a value of TRUE
#train used as part of the Caret library. Documentation found in tfestimators package 

In class with Ana follow-along

Lecture 3

review:

\(knn\)-regression

k-nearest neighbour, \(k\) = x - simple ML algo - based on calculating distances between observations - distance e.g. euclidean (euclidean sorting algo), MANHATTAN, \(ek\) in a multidimensional space of \(X\)(predictors) - predict by averaging dependant variables for the closest K-neighbours - k is tuned (we change oru k input based on whatever)

Today: Feature & target engineering (almost synonymous with data preprocessing)

  • Role
  • Place in the process
  • hands on \(ex\) in R

Pre-processing step:

Which is optimal in terms of resources(time) = alternative 2 which ideal in terms of accuracy? = alternative 3

if done at step 1 we average the dataset immediately. We want a pure untouched dataset without influence frmo our actions.

if done at step 2 we standardize the mean for the whole dataset. The same mean will be implemented in both the analyzis of the model and the evaluation. Doing it this way is good 

If done at step 3 we can do the integrations between each fold of the model. This increases the accuracy between each fold for the next parse. This takes a load of time due to multiple optimizations

- data leakage: in some way the process of modelling we affect the unseen observations with information from the training observations. Can skew dataset and impact your error.

When Y is skewed we want to transform it. Common transformation methods include - Log transformation (most common, doesn’t function on 0-values) - Box Cox transformation (most flexible, can be applied to positive and 0-values) - Yeo-Johnson transmformation (Handles negative values)

Predictions follow transformation scale (if we log transform result will also be log)

missing values 1

  • missing values can be informative (Kuhn and Johnson 2013)

    depend on data colelction and deserve own category

  • missing at random (Little and Rubin 2014) Can be deleted or imputed

    in R, typically NA or NaN, any any character can define a mising, blank space etc.

  • If you wish to divide variables and you divide by zero you can get a NA variable as you cannot divide by zero. Be aware of random missing data occuring

Missing values 2

  • Use plots to identify mising data bunch. useful for color coding

imputation 1

Imputation is used for replacing missing values with our best guesses.

Methods:

  • Estimated statistics: Mean, Median, mode ( for categorical)

  • K-nearest neighbours, Tree-based models (use knn to impute missing value, use the average value of the x nearest neighbours to estimate)

As Imputation is done within re-sampling it increases processing demands.

imputation 2

include_graphics("Imputation 2.png")

Mean imputation is a constant average, data doesn’t get a nice fit

KNN imputation estimates values better using other close values

Bagged trees (decision-tree model?) Also gets a great fit. Preserves relationship.

From here on out codeblocks in lecture 3 are related to the Feature_engineering R file from the lecture.

Feature filtering

Why?

  • Model with many features

    • Hard to interpret

      • Costly to compute (time, processing power)
  • Some models are inflexible to non-formative predicotrs (e.g.,) the lasso model and tree-based model). Others are negatively affected.

Here we are trying ot undertadn how much of the error is impacted by our non informative errors. Our non informative erroes are our X’s where we have many missing values or many invariable variables. If the data of the variables are missing or invariable it will have no impact on our DV.

  • remove zero or near-zero variance features

As seen on the graph, more zero or near-zero variance features increases RMSE

Tutorial 2

Problem 1: Programming The purpose of this problem is to get comfortable with R and its facilities. We shall spendmost of the time doing some basic computations. If you are a good programmer you will finishthese computations quickly. First start by opening R, create a new script and save it to your hard drive with the name: “Exercise1.R”.

Part 1


v1 <- c(1,2,2,1)
v2 <- c(2,3,3,2)

v1+v2
v1-v2
v1*v2
v3 <- c(v1,v2)

Part 2


#1 
m1 <- c(1,6,3,2,4,6)
mA <- matrix(m1,ncol=2)
mA

#2
print(mA[1,])
print(mA[2,])
print(mA[3,])
print(mA[,1])
print(mA[,2])

rowSums(mA)


apply(mA, 1, FUN=min) 
apply(mA, 1, FUN=max) 

sort(mA[,1],decreasing = FALSE)

#3

mD <- matrix(1:1, ncol= 4, nrow=4)

mD[c(1,6,11,16)] <- 0
mD

mD <- matrix(1, nrow=4, ncol=4)

diag(mD) <- rep(0, nrow(mD)) 
mD


mE <- matrix(1:16, ncol=4,nrow=4,byrow=TRUE)
mE
mE[-c(3,5,6,9,16)] <- 0

mE
mi <- diag(x=1, nrow=4, ncol=4)


#4

mF <- (rbind(mD,mE))
mF

mE+mD
mE*mD

mE %*% mD #matrix product



#5 
x = 1
calc_x <- {
    if(x <= 0) {
      print("-x^3") 
    } else {
      if(x > 1) { 
        print("sqrtx")
       } else {print("x^2")
       }
    }
}
       
calc_x

#6 busted måde
# h(x,n)=1 +x+x2+x3+···+xn=∑ni=0xi
# using replicate it's easy to match x to n

#func <- function(hxn)
 # {
#for (j in 1:n)
#{
#  x[j] = j^n
#}
# x
#}

#n = 6
#x_1 = n
#x = rep(x_1,n)

#func(hxn)

#6
func <- function(x,n)
{
  sum = 0
  
  for (j in 0:n)
  {
    sum = sum + x^j
  }
  return(sum)
}

func(x=1, n=2)

# 2^0 + 2^1 + 2^2 + 2^3

#7 fuck while loops
 
func2 <- function(x,n)
{
  sum = 0
  j = 0 
  while (j <= n)
  {
    sum = sum + x^j
    j = j + 1
  }
  return(sum)
}

func2(x=3, n=3)


func3 <- function(x,n)
{
x1 <- c(0:x)
print(x1)
{
  n1 <- (0:n)
  print(n1)
}
nx1 <- x1^n1
nx1
print(sum(nx1))
}

func3(x=3, n = 3)


  #8

# A room contains 100 toggle switches, originally all turned off.  100 people enter the roomin turn.  The first one toggles every switch, the second one toggles every second switch, theone third every third switch, and so on until the last person, who toggles the last switch only.  At the end of this process, which switches are turned on?Note:This requires alittle thinking. Don’t give up!
  
#rest state = 100 off
#first pass = 100 on
#second pass = 50 on, c(1:100,2) is on
#third pass = 

##8 person \(i\) will flip lights bulbs that are multiples of i

\[ i \in ({1,2,3 … 100}) \]

therefor;

\[ i,j \in ({1,2,3 … 100}) \]

Lightbulb 5 will be flipped by people that are a factor of 5 (5 is prime number)

\[ i,5 \in ({1,5}) \] lightbulb 10 will be flipped by people that a factor of 10 \[ i,5 \in ({1,2,5,10})\]

Light bulb 40 will be flipped by people that are a factor of 40 \[ i,5 \in (1, 2, 4, 5, 8, 10, 20, 40) \]

Example: Light bulb 25 will be flipped by factorials of 25 \[ i,25 \in ({1,5,25}) \] State 1, all lightbulbs are off

state 2, all lightblubs are on (person 1)

state 5, every fifth light bulb is swapped. 25 is now off (person 5)

state 25, every twenthyfifth light blub is swapped. 25 is now on (person 25)

Only the factorials of x impacts status.

Knowing this we define can make the function

which(!x)
 [1]   1   4   9  16  25  36  49  64  81 100

We can also use the rep function as commented above for #6 for nicer syntax

Line for Line:

x = rep(1,100), define x as 1 repeated 100 times (100 lightbulbs)

define the for metric, here for i in the range of x, 1:100-1. We use -1 here to gain 0 and 1 values, which the xor command then translates to true or false. Try printing after each line to see how x behaves.

for a rep sequence, length.out = non-negative integer. The desired length of the output vector. Other inputs will be coerced to a double vector and the first element taken. Ignored if NA or invalid..

Therefor length out dictates the amount of times we run the sequence.

try playing around with with the function.

which(!x)
 [1]   1   4   9  16  25  36  49  64  81 100

Problem 2, Linear regression

problem 2.1

Consider the standard linear regression model \[Y=Xβ+ε\] where \(Y\) is an-dimensional outcome vector, \(X\) an \(n×p\) dimensional regressor matrix, \(β\) a \(p\)-dimensional vector of coefficients, and \(ε\) an \(n\)-dimensional error term vector. The OrdinaryLeast Squares estimator for \(β\) is given by

\[hat{β}= (X′X)−1X′Y\]

where \(−1\) denote the matrix inverse and′is the matrix transpose. The predictions from a linearregression model are given by

\[\hat{Y}=X\hat{β}\]

the residuals are given by

\[e=Y−\hat{Y}\]

The error variance is estimated as

\[ \hat{σ}^2= \frac{1}{n-p} \sum_{i=1}^{n}e_i^2\] where \(ei\) denote the \(i-th\) entry of \(e\). The variance of \(\hat{β}\) under full ideal conditions can be estimated as \[\hat{V}[\hat{β}] = \hat{σ}^2(X′X)^-1 \] Write a functionfOLS()that estimates a linear regression model and provides estimation results. It should have the following inputs:

  • An data frame containing Yand X

  • The label or column entry corresponding to the dependent variableY

  • The labels or column entries corresponding to the regressorsX

    It should return a list object containing:

  • The numerical vector of coefficient estimatesˆβ

  • The numerical vector of predictionsˆY

  • The estimated error variance ˆσ2

  • The standard errors forˆβ(Hint:These are the square-roots of the diagonal entries ofˆV[ˆβ])


<- c(1:100)
Y <- c(2:100,2)
dfols <- data.frame(X,Y)
head(dfols)

fOLS <- function() {
  
  
  
}
print(x)
LS0tCnRpdGxlOiAiQWxsX2xlY3R1cmVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKTWF0aCBvcGVyYXRvcnMgZm91bmQgYXQKPGh0dHBzOi8vcnBydWltLmdpdGh1Yi5pby9zMzQxL1MxOS9mcm9tLWNsYXNzL01hdGhpblJtZC5odG1sPgoKYGBge3J9Cgo/TWF0cml4CmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KHRpZHltb2RlbHMpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShyc2FtcGxlKQoKCmBgYAoKIyBMZWN0dXJlIDMgUEggKyBpbiBjbGFzcwoKIyMgSW4gY2xhc3MgYXNzaWdubWVudAoKIyMjIFByb2JsZW0gMS4xLzEuMgoKYGBge3J9CmJvc3RvbiA8LSByZWFkLmRlbGltKCJib3N0b25fY29ycmVjdGVkLnR4dCIsIHNraXAgPSA5LCBoZWFkZXIgPSBUUlVFKQpkaW0oYm9zdG9uKQpzdW0oaXMubmEoYm9zdG9uKSkKCmBgYAoKIyMjIHByb2JsZW0gMwoKJVw+JSBpcyBhIHBpcGluZyBmdW5jdGlvbi4gUmF0aGVyIHRoYW4gc3BlY2lmeWluZyB0aGUgdmFyaWFibGUgaSB3YW50IHRvCnRhcmdldCBmb3IgYWxsIG9wZXJhdGlvbnMgSSBoYXZlIGFscmVhZHkgcHJvbXB0ZWQgdGhlIHVwZGF0ZWQgdmFyaWFibGUuClN5bnRheCBib3N0b24gXDwtCnNlbGVjdChib3N0b24sLSJPQlMuIiwtIlRPV04iLC0iVE9XTi4iLC0iVFJBQ1QiLC0iTE9OIiwtIkxBVCIsLSJNRURWIikKd291bGQgZ2l2ZSBzYW1lIHJlc3VsdC4gUGlwaW5nIGlzIHVzZWZ1bCB3aGVuIHlvdSB3aXNoIHRvIHRvIGFkZGl0aW9uYWwKdmFyaWFibGUgdXBkYXRlcyBpbiBhIHJvdy4KCmBgYHtyfQpib3N0b24gPC0gYm9zdG9uICU+JQogIHNlbGVjdCgtIk9CUy4iLC0iVE9XTiIsLSJUT1dOLiIsLSJUUkFDVCIsLSJMT04iLC0iTEFUIiwtIk1FRFYiKQpgYGAKCiMjIyBwcm9ibGVtIDQKCnJlbWVtYmVyIHRvIHNhdmUgdmFyaWFibGUgY2hhbmdlIHRvIHRoZSB1cGRhdGVkIHZhcmlhYmxlIHVzaW5nIFw8LQoKQmUgYXdhcmUgdGhhdCBpbiB0aGlzIGV4YW1wbGUgdGhlIGxvZ2ljIGlzOgoKTmFtZXMoYm9zdG9uKSAtIHRoZSBjb2x1bW5zIG9mIGRmIGJvc3RvbiAtLCBuZWVkcyB0byBiZSBzZXQgdG8gbG93ZXIuIFdlCm5lZWQgdG8gYm90aCBzYXZlIHRoZSBsb3dlcmNhc2UgbmFtZXMgdG8gb3VyIGRhdGFmcmFtZSBhbmQgc3BlY2lmeSB3ZQp3YW50IHRoZW0gbG93ZXIsIGFzIHN1Y2ggbmFtZXMoYm9zdG9uKSkgaXMgY2FsbGVkIHR3aWNlLgoKYGBge3J9Cm5hbWVzKGJvc3RvbikgPC0gdG9sb3dlcihuYW1lcyhib3N0b24pKQoKYGBgCgojIyMgcHJvYmxlbSA1CgpgYGB7cn0KYm9zdG9uIDwtIHJlbmFtZShib3N0b24sbWVkdj1jbWVkdikKaGVhZChib3N0b24pCmBgYAoKIyMjIHByb2JsZW0gNgoKSSdtIHVzaW5nIHRoZSByZWFkciB3cml0ZV9jc3YgZnVuY3Rpb25hbGl0eS4gSXQncyBzbWFydCBhbmQga25vd3Mgd2hhdAp0byBkbyByZWdhcmRpbmcgaGVhZGVycyBldGMgYWxyZWFkeS4gSWYgeW91IHdlcmUgdG8gdXNlIGJhc2UgUiBJIHdvdWxkCmRvIHRoZSBmdW5jdGlvbiBhcyBmb2xsb3dzCgp3cml0ZS5jc3YoYm9zdG9uLCBmaWxlID0gIkJvc3RvbkJJLmNzdiIsIG5hID0gIk5BIikgc3BlY2lmeWluZyBvZgphZGRpdGlvbmFsIHBhcmFtZXRlcnMKCmBgYHtyfQp3cml0ZV9jc3YoYm9zdG9uLCAiQm9zdG9uQkkuY3N2IikKCmBgYAoKIyMjIFByb2JsZW0gMi4yCgpgYGB7cn0KYm9zdG9uMSA8LSBib3N0b24gJT4lCiAgc2VsZWN0KHRheCwgbWVkdikKICBzdW1tYXJ5KGJvc3RvbjEpCiAgY29yKGJvc3RvbjEpCgpgYGAKCiMjIyBQcm9ibGVtIDMKCmBgYHtyfQpnZ3Bsb3QoYm9zdG9uMSwgYWVzKHg9dGF4KSkgKyBnZW9tX2RlbnNpdHkoKSArIGxhYnMoeD0iVVMkIikKYGBgCgojIyMgUHJvYmxlbSA0CgpgYGB7cn0KZ2dwbG90KGJvc3RvbjEsIGFlcyh4PXRheCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1KQpgYGAKCiMjIyBwcm9ibGVtIDUKCk5lZWQgdG8gYXR0YWNoIHRoZSBkYXRhZnJhbWUgaW4gb3JkZXIgdG8gZG8gdGhlIGZhY3RvcmlhbCBvcGVyYXRpb25zLgpDb3VsZCBwcm9iYWJseSBiZSBzb2x2ZWQgYnkgcGlwaW5nPyBXaWxsIGFkZCB0byBsYXRlci4KCkZpcnN0IHdlIGRlZmluZSBvdXIgc3RyaW5nIHZhbHVlIGZhY3RvcmlhbCwgdGF4bGFiZWwuIFRoZW4gd2UgZGVmaW5lIHRoZQp2YWx1ZSBwYXJhbWV0ZXJzIGFzc2lnbmVkIHRvIHRoZSBzdHJpbmcuIElmIHRheCBpcyBsZXNzIHRoYW4gMzAwLCB0aGUKdmFsdWUgYXNzaWduZWQgaXMgbG93LCBvciAwLiBJZiBiZXR3ZWVuIDMwMCBhbmQgNjAwIHZhbHVlIGFzc2lnbmVkIGlzCm1lZGl1bSwgb3IgMS4gSWYgYWJvdmUgNjAwIHZhbHVlIGFzc2lnbmVkIGlzIGhpZ2gsIG9yIDMuCgpUaGUgZmFjdG9yKHRheF9kaXNjcmV0ZSwwOjIsdGF4bGFiZWwpIGRlZmluZXMgdGhlIHJhbmdlIHNwcmVhZCBmb3IKc3RyaW5nIHZhbHVlcyBvZiB0aGUgZmFjdG9yaWFsLgoKYGBge3J9CmF0dGFjaChib3N0b24pCnRheGxhYmVsIDwtIGMoImxvdyIsIm1lZGl1bSIsImhpZ2giKQp0YXhfZGlzY3JldGUgPC0gMCArICh0YXggPiAzMDApICsgKHRheCA8IDYwMCkKdGF4MiA8LSBmYWN0b3IodGF4X2Rpc2NyZXRlLDA6Mix0YXhsYWJlbCkKCmJvc3RvbiA8LSBjYmluZChib3N0b24sdGF4MikKCmhlYWQoYm9zdG9uKQoKCmBgYAoKIyMjIFByb2JsZW0gNgoKQ29vcmRfZmxpcCBmbGlwcyB0aGUgeCBhbmQgeSB2YWx1ZXMuIFVzZWQgaGVyZSBmb3IgaW5jcmVhc2VkCnJlYWRhYmlsaXR5LgoKYGBge3J9CmdncGxvdChib3N0b24sIGFlcyh4PXRheCx5PW1lZHYpKSArIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpCmBgYAoKIyMjcHJvYmxlbSA3IGdncGxvdCBzeW50YXggPSBhZXMgPSBBZXN0aGV0aWMgKHVkc2VlbmRlKSBnZW9tID0gdHlwZSBvZgpjaGFydCwgcG9pbnQgPSBzY2F0dGVyLCBib3hwbG90ID0gYm94cGxvdCwgaGlzdG9ncmFtID0gaGlzdG8uCnN0YXRfc21vb3RoIGlzIGEgYmVzdCBmaXQgbW9kZWwsIHlvdSBjYW4gZGVmaW5lIG1ldGhvZCBvZiB0aGUgZml0IGJ5CihtZXRob2Q9IngiKSwgaGVyZSAibG0iIGlzIGxpbmVhciBtb2RlbCBmaXQKCmBgYHtyfQpnZ3Bsb3QoYm9zdG9uLCBhZXMoeD10YXgseT1tZWR2KSkgKwogIGdlb21fcG9pbnQoKSArIAogIHlsaW0oMCw1MCkgKyAKICAjIHN0YXRfc21vb3RoKCkgCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJsbSIpCgoKYGBgCgojIExlY3R1cmUgNAoKIyMgUHJlZGljdGl2ZSBsZWFybmluZ3MKCklucHV0ID0gaW5kZXBlbmRlbnQgdmFyaWFibGVzIChJVikgPSBmZWF0dXJlcyA9IHByZWRpY3RvcnMgPSBYCgokJHhfMSzigKZ4X24kJAoKT3V0cHV0ID0gRGVwZW5kZW50IHZhcmlhYmxlcyAoRFYpID0gcmVzcG9uc2UgPSBZCgokJFlfMSzigKYseV9tJCQKCm90aGVyIHZhci4gdGhhdCBhZmZlY3QgWSwgYnV0IHRob3NlIHZhbHVlcyBhcmUgbmVpdGhlciBvYnNlcnZlZCBub3IKY29udHJvbGxlZCAobm9pc2U/KQoKJCRaXzEs4oCmWl9rJCQKCiMjIyBNYXRlbWF0aGljYWwgbW9kZWwKCiQkICgxKSB5X2sgPSBnX2soeDEsLi4uLCB4X24sIHpfMSwgel9MKSwgPWsgPSAxLEsgJCQKCiR5X2skIGNhbiBiZSBhbnkgcm93IGluIG91ciBkYXRhc2V0CgojIyMgU3RhdGlzdGljYWwgbW9kZWwKCiQkICgyKSAgeV9rID0gZl9rKHhfMSwgLi4uLiAsIHhfbikgKyBlX2ssICBrID0gMSxLICQkCgokZl9rJCA9IEZ1bmN0aW9uIG9mIHRoZSBvYnNlcnZlZCBpbnB1dHMgJCQgZl9rICQkCgokZV9rJCA9IGFuIGFkZGl0aW9uYWwgcmFuZG9tIHN0b2NoYXN0aWMgY29tcG9uZW50IC8gZXJyb3IgdGVybSAkJCBlX2sgJCQKCklmIGRlbm90aW5nICQkWCA9ICh4XzEseF8yLC4uLix4X3ApJCQgdGhlbiAoMikgYmVjb21lcwokJHlfayA9IMaSKFgpICsgZV9rJCQgZXZlbiBpZiB3ZSBmaW5kIHRoZSBwZXJmZWN0IGFwcHJveGltYXRpb24gb2YgJMaSKFgpJAp3ZSB3aWxsIG5ldmVyIGJlIGFibGUgdG8gY29tcHV0ZSBmb3IgdGhlIHJhbmRvbSBmYWN0b3IgJGVfayQKCiRmJCBJcyB1c2VkIGFzIGFuIGVzdGltYXRpb24gZm9yIG5ldyB5IG9ic2VydmF0aW9ucywgd2hpY2ggaGVscHMgdXMKdW5kZXJzdGFuZCB0aGUgbWVjaGFuaXNtIHRoYXQgaXMgcHJvZHVjZWQgYnkgdGhlIGRhdGEgKHkpIG91dHB1dCB0byBoZWxwCmludGVydmVuZSBpbiB0aGUgZnV0dXJlCgpFeGFtcGxlIDEoc2xpZGUgMTQpIChJU0wgcC4gMTYtMTcpIGlzIGFuIE9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgKE9MU1MpCnJlZ3Jlc3Npb24gRXhhbXBsZSAyKHNsaWRlIDE1KSAoSVNMLCBwLiAxNi0xNykgT0xTIGVzdGltYXRpb24gY2FuIGJlCnZpZXdlZCBhcyBhIHByb2plY3Rpb24gb250byB0aGUgbGluZWFyIHNwYWNlIHNwYW5uZWQgYnkgdGhlIHJlZ3Jlc3NvcnMuCgpGb3IgcHJlZGljdGlvbnM6IC0gRm9jdXMgb24gcmVkdWNpYmxlIGVycm9ycwoKaWY6ICQkIEUoWS3FtileMiA9IEVbZihYKStlLWbMgihYKV1eMiQkIFRoZW4KJCQgRShZLcW2KV4yID0gW2YoWCktZsyCKFgpXV4yK1ZhcihlKSQkCgpXaGVyZSAkW2YoWCktZsyCKFgpXV4yJCBpcyByZWR1Y2libGUgYW5kICRWYXIoZSkkaXMgaXJyZWR1Y2libGUKCkZvciBpbmZlcmVuY2UgcHV0IGZvY3VzIG9uIC0gV2hpY2ggcHJlZGljdG9ycyAkWCQgYXNzb2NpYXRlIHdpdGgKcmVzcG9uc2UgJHkkIC0gTWFnbml0dWRlICYgZGlyZWN0aW9uIC0gcmVsYXRpb25zaGlwIChMaW5lYXIgb3Igb3RoZXIpIC0KaW50ZXJhY3Rpb24gZWZmZWN0cwoKKipJbiBNTCB3ZSdyZSBtYWlubHkgZm9jdXNlZCBvbiB0aGUgcHJlZGljdGl2ZSBwZXJzcGVjdGl2ZSByYXRoZXIgdGhhbgp0aGUgaW50ZXJmZXJlbmNlKiogLSBJdCBpcyBob3dldmVyIHBvc3NpYmxlIGluIHNvbWUgc2l0dWF0aW9ucyB0byBmb2N1cwpvbiBib3RoIGFzcGVjdHMgYXQgb25jZQoKRVNUSU1BVElPTiBPRiDGkgoKMS4gIFBhcmFtZXRyaWMKMi4gIE5vbi1wYXJhbWV0cmljCgoqMS4gUGFyYW1ldHJpYyoKCioqJ2EgcHJpb3JpIGFzc3VtcHRpb24iIDogUmVsYXRpbmcgdG8gcm8gZGVub3RpbmcgcmVhc29uaW5nIG9yIGtub3dsZWRnZQp3aGljaCBwcm9jZWVkcyBmcm9tIHRoZW9yZXRpY2FsIGRlZHVjdGlvbiByYXRoZXIgdGhhbiBvYnNlcnZhdGlvbiBvcgpleHBlcmllbmNlLCBpZS4gInNleHVhbGl0eSBtYXkgYmUgYSBmYWN0b3IgYnV0IGl0IGNhbm5vdCBiZSBhc3N1bWVkIGEKcHJpb3JpIioqCgpXZSBmaXQgdGhlIG1vZGVsIGJhc2VkIG9uIG91ciBhIHByaW9yaSBhc3N1bXB0aW9ucy4gSWYgd2UgZXhwZWN0IGxpbmVhcgpmaXQgd2UgZXN0aW1hdGUgdGhlIHBhcmFtZXRlcnMgYmV0YSAoTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24uIElmIHlvdQp1c2Ug4omIIHlvdSBkb24ndCBub3RlIHRoZSBlcnJvciB2YXJpYWJsZSAkZSQgYXMgaXQncyBhbiBlc3RpbWF0aW9uKQoKJCQgeSDiiYggw59fbyvDn18xIFhfMSvDn18yIFhfMivigKYrw59fcCBYX3AgJCQgd2hlcmUgJMOfXzAsw59fMSzDn18yJCBhbmQgJMOfX3AkCmFyZSBvdXIgZXN0aW1hdG9ycwoKRm9yIGV4YW1wbGUgMSBzbGlkZSAyMyAkaW5jb21lIOKJiCDDn18wK8OfXzFZb0VfMSArw59fMlNlbmlvcml0eV8yJAoKZm9yIGV4YW1wbGUgMiBzbGlkZSAyNCAoUG9seW5vbWluYWwgYW5kIGludGVyYWN0aW9uIGluY2x1ZGVkKSwoRm9sbG93dXAKYWZ0ZXIgY2xhc3MsIGluY29ycmVjdCkgJFBJIOKJiCDDn19vK8OfXzEgWF8xK8OfXzIgWF8yK+KApivDn19wIFhfcCQuIFRoZQppbnRlcmFjdGlvbiBjaGFuZ2VzIHRoZSBjdXJ2YXR1cmUgb2YgdGhlIDItZGltZW5zaW9uYWwgcGxhbmUuCgoqMi4gTm9uLXBhcmFtZXRyaWMqCgpObyBhc3N1bXB0aW9uOyAkxpIkIGlzIHZlcnkgZmxleGlibGUKCkFkdmFudGFnZXM6IFByZWRpY3RpdmUgYWNjdXJhY3kgRGlzYWR2YW50YWdlczogTGFyZ2UgbnVtYmVyIG9nCm9ic2VydmF0aW9ucyBpcyByZXF1aXJlZDsgb3ZlcmZpdHRpbmcgcmlzazsgbG93IGludGVycHJldGFiaWxpdHkuIE5vbiBhCnByaW9yaQoKZXhhbXBsZSBmcm9tIGNsYXNzOiBDcnVtYmxlZCB1cCBwYXBlci4gSW1hZ2luZSB3ZSBoYXZlIHRvIHByZWRpY3QgdGhlCnBsYW5lIG9mIHRoZSBwYXBlciwgaXQncyBpbmNyZWRpYmx5IGRpZmZpY3VsdCBhcyB0aGUgcGFwZXIgaXMgYWxsCmNyYW1ibGVkIHVwLiBTb21ldGltZXMgaW4gbm9uLXBhcmFtZXRyaWMgJMaSJCBjYW4gYmUgY29uc2lkZXJlZCBhCmJsYWNrYm94IGFzIGl0J3MgYm9yZGVybGluZSBpbXBvc3NpYmxlIHRvIGFwcHJveGltYXRlLgoKKndpbGwgYmUgY292ZXJlZCBpbiBNYWNoaW5lIExlYXJuaW5nIDIqCgojIyBtZXRob2Qgc3VpdGFiaWxpdHkKCmBgYHtyfQppbmNsdWRlX2dyYXBoaWNzKCJmbGV4aWJpbHR5IHZzIGludGVycC5wbmciKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRGF0YSBwYXJ0aXRpb25pbmcKClNwbGl0IGRhdGFzZXRzIGludG8gcGFydGl0aW9ucywgb25lIGZvciB0cmFpbmluZyBhbmQgb25lIGZvciB0ZXN0aW5nCkV4YW1wbGU6IEZyb20gVHV0b3JhbCAxIHdlIGtub3cgdGhhdCAoZGF0YXNldCBub3QgaW5jbHVkZWQpCgpgYGB7cn0KIyBsaWJyYXJ5KHJzYW1wbGUpCiMgc2V0LnNlZWQoMTIzKSAKIyBzcGxpdF8xICA8LSBpbml0aWFsX3NwbGl0KGRmLCBwcm9wID0gMC42KQojIHRyYWluIDwtIHRyYWluaW5nKHNwbGl0XzEpCiMgdGVzdCA8LSB0ZXN0aW5nKHNwbGl0XzEpCmBgYAoKRm9yIGFib3ZlOyBMb2FkIHJzYW1wbGUgbGlicmFyeSBmb3IgdGhlIHV0aWx0aWVzCgpTZWVkIGZvciByZXByb2R1Y3RpYmlsaXR5IC0gVGhlIHNlZWQgc3BlY2lmaWVzIHRoZSBwb2ludCBhdCB3aGljaCB3ZQp3b3VsZCBsaWtlIHRvIHNwbGl0IHRoZSBkYXRhc2V0LiBXaXRob3V0IGEgc2VlZCBhIHJhbmRvbSBudW1iZXIgd2lsbCBiZQphc3NpZ25lZCAobnVtYmVyIGFzc2lnbmVkIGlzIG5vdCBhY3R1YWxseSByYW5kb20gYnV0IHJhdGhlcgpwc2V1ZG9yYW5kb20pCgpkZWZpbmUgdmFyaWFibGUgb2Ygc3BsaXQgPSBzcGxpdF8xLCB3aGVyZSBkZiBpcyBvdXIgZGF0YWZyYW1lIG9yIHRhYmVsLgoKUHJvcCBkZWZpbmVzIHdoZXJlIHRoZSBkYXRhc2V0IGlzIHNwbGl0LCAwLjYgPSA2MC80MCwgMC41IHdvdWxkIGVxdWFsCjUwLzUwLCBldGMuCgp0cmFpbmluZyBhbmQgdGVzdGluZyBhcmUgZnVuY3Rpb25zIG9mIHRoZSByc2FtcGxlIGxpYnJhcnkuIHVzaW5nIHRoZXNlCmZ1bmN0aW9ucyB3aXRoIG91ciBzcGxpdCB2YXJpYWJsZSB3aWxsIGF1dG9tYXRpY2FsbHkgYXNzaWduIHRoZSBkZXNpcmVkCnNwbGl0CgoqKkFwcHJvYWNoZXMgZm9yIHBhcnRpdGlvbmluZyoqwqgKCnNwbGl0cyB3aWxsIHR5cGljYWxseSBiZSBkb25lIGluIGFuIDgwOjIwIGZhc2hpb24gKHByb3AgPSAwLjgpCgooMSkgUmFuZG9tIHNwbGl0XAoKLSAgIFRyYWR0aW9uYWwgdGVjaG5pcXVlLCBzaW1wbGVzdCB3YXkuIFVzZWQgaW4gdGhlIGJvb2suCgooMikgU3RyYXRpZmllZCBzcGxpdAoKLSAgIENvbnNpZGVycyB0YXJnZXQgdmFyaWFibGUoJHkkKSBhbmQgd2lsbCB0cnkgdG8gZ3JvdXAgZm9yIGl0IGJlZm9yZQogICAgc3BsaXQgLSBpZiBkYXRhc2V0IGhhcyAiMSIgYW5kICIwIiB2YXJsdWVzIGFuZCB5b3UgcGljayByYW5kb21seSBpdAogICAgY2FuIHNwbGl0IHRoZSBzZXRzIHBvb3JseSwgaGVyZSBncm91cGluZyB0aGVtIGJlZm9yZWhhbmQgZW5zdXJlcwogICAgZGF0YSBpbnRlZ3JpdHkgZm9yIGJvdGggc2V0cwoKIyMgZXhwYW5kIHdpdGgga25vd2xlZGdlIGZyb20gPGh0dHBzOi8vYnJhZGxleWJvZWhta2UuZ2l0aHViLmlvL0hPTUwvcHJvY2Vzcy5odG1sPgoKUmUtc2FtcGxpbmcKCmBgYHtyfQppbmNsdWRlX2dyYXBoaWNzKCJNTCBwcm9jZXNzLnBuZyIpCmBgYAoKLSAgIFNpbmdsZSB0cmFpbmluZyBkYXRhIGxlYWRzIHRvIGluYWNjdXJhdGUgcmVzdWx0cy4gVG8gYXZvaWQgdGhpcyB3ZQogICAgdXRpbGl6ZSByZS1zYW1wbGl1bmcgbWV0aG9kcwoKICAgIC0gICBub3RlIGZyb20gbGVjdHVyZSA0OiBEYXRhIHByZS1wcm9jZXNzL2ZlYXR1cmUgZW5naW5lZXJpbmcKICAgICAgICBoYXBwZW5zIGJlZm9yZSB5b3UgZG8geW91ciBkYXRhIHNwbGl0cy4gSWYgeW91IHdhbnQgdG8gaW1wYWN0CiAgICAgICAgeW91ciBkYXRhIGluIGEgc3BlY2lmaWMgd2F5IGFmdGVyIHNwbGl0dGluZyB0byB0ZXN0IGRpZmZlcmVudAogICAgICAgIHJlc3VsdHMgeW91IGNhbiBkbyBpdCBhZnRlciBlc3RhYmxpc2hpbmcgc3BsaXQuIEluIHByYWN0aWNlIGl0J3MKICAgICAgICBkb25lIGJldHdlZW4gZXZlcnkgc3RlcCBpbmNsdWRpbmcgbW9kZWxsaW5nLiBkb2luZyB0aGUKICAgICAgICBwcmUtcHJvY2Vzc2luZyBsYXRlciBpcyBtb3JlIG9wdGltYWwgYXMgeW91IGdldCBmdXJodGVyIGluc2lndAogICAgICAgIGludG8geW91ciBkYXRhLgoKICAgIGlmIGRvbmUgYXQgc3RlcCAxIHdlIGF2ZXJhZ2UgdGhlIGRhdGFzZXQgaW1tZWRpYXRlbHkuIFdlIHdhbnQgYSBwdXJlCiAgICB1bnRvdWNoZWQgZGF0YXNldCB3aXRob3V0IGluZmx1ZW5jZSBmcm1vIG91ciBhY3Rpb25zLgoKICAgIGlmIGRvbmUgYXQgc3RlcCAyIHdlIHN0YW5kYXJkaXplIHRoZSBtZWFuIGZvciB0aGUgd2hvbGUgZGF0YXNldC4gVGhlCiAgICBzYW1lIG1lYW4gd2lsbCBiZSBpbXBsZW1lbnRlZCBpbiBib3RoIHRoZSBhbmFseXppcyBvZiB0aGUgbW9kZWwgYW5kCiAgICB0aGUgZXZhbHVhdGlvbi4gRG9pbmcgaXQgdGhpcyB3YXkgaXMgZ29vZAoKICAgIElmIGRvbmUgYXQgc3RlcCAzIHdlIGNhbiBkbyB0aGUgaW50ZWdyYXRpb25zIGJldHdlZW4gZWFjaCBmb2xkIG9mCiAgICB0aGUgbW9kZWwuIFRoaXMgaW5jcmVhc2VzIHRoZSBhY2N1cmFjeSBiZXR3ZWVuIGVhY2ggZm9sZCBmb3IgdGhlCiAgICBuZXh0IHBhcnNlLiBUaGlzIHRha2VzIGEgbG9hZCBvZiB0aW1lIGR1ZSB0byBtdWx0aXBsZSBvcHRpbWl6YXRpb25zLgoKICAgIC0gICBkYXRhIGxlYWthZ2U6IGluIHNvbWUgd2F5IHRoZSBwcm9jZXNzIG9mIG1vZGVsbGluZyB3ZSBhZmZlY3QgdGhlCiAgICAgICAgdW5zZWVuIG9ic2VydmF0aW9ucyB3aXRoIGluZm9ybWF0aW9uIGZyb20gdGhlIHRyYWluaW5nCiAgICAgICAgb2JzZXJ2YXRpb25zLiBDYW4gc2tldyBkYXRhc2V0IGFuZCBpbXBhY3QgeW91ciBlcnJvci4KCioqay1mb2xkIGNyb3NzIHZhbGlkYXRpb24qKgoKV2UgY2FuIGZvcmNlIHRoZSB0cmFpbmluZyBzZXRzIHRvIGJlIHN0cmF0aWZpZWQgdGhyb3VnaG91dCB0aGUgay1mb2xkCmNyb3NzIHZhbGlkYXRpb24uCgpgYGB7cn0KaW5jbHVkZV9ncmFwaGljcygiay1mb2xkIG1ldGhvZC5wbmciKQpgYGAKCioqYm9vdHN0cmFwcGluZyoqCgpleHRyYWN0aW5nIG9mIG9ic2VydmF0aW9ucyB3aXRoIHJlcGxhY2VtZW50LiBvdXQgb2YgdGhpcyBkYXRhc2V0IHlvdSBjYW4KZXh0cmFjdCBzYW1wbGVzLCBwdXQgdGhlbSBiYWNrLCBzYW1lIG9ic2VydmF0aW9uIGNhbiBiZSBleHRyYWN0ZWQKbXVsdGlwbGUgdGltZXMuIFBoaWxsaXAgd2lsbCB0b3VjaCBvbiB0aGlzIGxhdGVyLgoKYGBge3J9CmluY2x1ZGVfZ3JhcGhpY3MoIkJvb3RzdHJhcHBpbmcucG5nIikKYGBgCgpVc2UgbXVsdGlwbGUgc3BsaXQgd2hlbiBwcmFjdGljaW5nIHRvIGdldCBiZXN0IHJlc3VsdHMKCktub3dpbmcgdGhpcywgdGhlIHN0YW5kYXJkIHByb2NlZHVyZSBmb3IgbW9kZWwgYnVpbGRpbmcgc2hvdWxkIGJlCgpgYGB7cn0KaW5jbHVkZV9ncmFwaGljcygiUHJvY2VkdXJlIG1vZGVsbGluZy5wbmciKQpgYGAKCiMjIE1vZGVsIGV2YWx1YXRpb24gY3JpdGVyaWEKCiMjI3dpbGwgYmUgZXhwYW5kZWQgdXBvbgoKZXhwYW5kIHdpdGgga25vd2xlZGdlIGZyb20KPGh0dHBzOi8vYnJhZGxleWJvZWhta2UuZ2l0aHViLmlvL0hPTUwvcHJvY2Vzcy5odG1sPgoKKipSZWdyZXNzaW9uIG1vZGVsczoqKgoKKmJvbGQqID0gbW9zdCBjb21tb24KCi0gICAqKk1TRSoqIChtZWFuIHNxdWFyZWQgZXJyb3IpCgotICAgKipSTVNFKiogKHJvb3QgbWVhbiBzcXVhcmVkIGVycm9yKQoKLSAgIERldmlhbmNlCgotICAgTUFFCgotICAgUi1zcXVhcmVkCgoqKkNsYXNzaWZpY2F0aW9uIG1vZGVscyoqCgotICAgTWlzY2xhc3NpZmljYXRpb24gcmF0ZQoKLSAgIE1lYW4gcGVyIGNsYXNzIGVycm9yCgotICAgTVNFCgotICAgQ3Jvc3MtZW50cm9weQoKLSAgIEdpbmkgaW5kZXgKCi0gICBDb25mdXNpb24gbWF0cmljCgotICAgQWNjdXJhY3ksIFByZWNpc2lvbiwgU2Vuc2l0aXZpdHkvUmVjYWxsLCBTcGVjaWZpY2l0eQoKLSAgIFJPQyBhbmQgQVVDXCoKCiMjIEVycm9ycwoKVHJhaW5pbmcgZXJyb3JzKE1TRSkKJCRNU0UgPSBcZnJhY3sxfXtufSBcc3VtX3tpID0gMX1ee259ICh5X2kgLSBmzIIoeF9pKSleMiAkJCBUcmFpbmluZwplcnJvcihSTVNFKSAkJFxzcXJ0IE1TRSQkCgpUZXN0aW5nIGVycm9yICh0ZXN0IE1TRSkgJCRBdmUoeV9vLWbMgih4X28pKV4yJCQgd2hlcmUgJHhfbyx5X28kIGFyZSBvYnMuCm5vdCB1c2VkIHRvIHRyYWluLiBPdGhlcndpc2UgZm9ybXVsYXMgYXJlIHByZXR0eSBtdWNoIHRoZSBzYW1lCgojIyBvdmVyZml0dGluZwoKU21hbGwgdHJhaW4gZXJyb3IKCipIaWdoIHRlc3QgZXJyb3IqCgpPdXIgbW9kZWwoYWxnb3JpdGhtKSBpcyB0cnlpbmcgdG9vIGhhcmQgdG8gZmluZCBhIHN1aXRlZCBmaXQKCipWYXJpYW5jZSBvZiBmaXQqID0gQW1vdW50IGJ5IHdoaWNoICRmzIIkIHdvdWxkIGNoYW5nZSBpZiBlc3RpbWF0ZWQgdXNpbmcKZGlmZmVyZW50IHRyYWluaW5nIHNldAoKKmJpYXMgb2YgZml0KiA9IGVycm9yIGludHJvZHVjZWQgYnkgYXBwcm94aW1hdGluZyByZWFsLWxpZmUgcHJvYmxlbSBpbgpzaW1wbGUgbW9kZWxzCgoqYWltIHRvIG1pbmltaXplIGJvdGgqCgokJEUoeV8wLWbMgih4X28pKV4yID0gVmFyKGbMgih4X28pKSArIFtiaWFzKGbMgih4X28pKV1eMiArIFZhcihlKSQkCgp3aGVyZQoKJEUoeV8wLWbMgih4X28pKSQgPSBFeHBlY3RlZCB0ZXN0IE1TRQoKJFZhcihmzIIoeF9vJCA9IFZhcmlhbmNlIG9mIGZpdAoKJFtiaWFzKGbMgih4X28pKV0kID0gQmlhcyBvZiBmaXQKCiRWYXIoZSkkID0gSXJyZWR1Y2libGUgZXJyb3IKCmBgYHtyfQppbmNsdWRlX2dyYXBoaWNzKCJNZXRob2QgY2hvaWNlIGV4YW1wbGUucG5nIikKYGBgCgpPbiB0aGUgZXhhbXBsZSB3ZSBoYXZlIHRocmVlIG1vZGVscyBmaXR0ZWQuIERhdGFzZXQgd2FzIHNpbXVsYXRlZCBiYXNlZApvbiBibGFjayAodHJ1ZSAkxpIkKS5TbW9vdGhpbmcgc3BpbmVzIG9uIGxlZnQgZ3JhcGggaXMgdG9vIGFnZ3Jlc3NpdmVseQp0cnlpbmcgdG8gZml0IGRhdGFwb2ludHMuCgpGb3IgdGhlIGxpbmVhciByZWdyZXNzaW9uIHRoZSB0cmFpbmluZyBlcnJvciBpcyB0aGUgbG93ZXIgeWVsbG93IHNxdWFyZSwKdGVzdGluZyBlcnJvcnIgdXBwZXIgeWVsbG93IHNxdWFyZS4gVGhlc2UgYXJlIHRoZSBlcnJvcnMgd2hlbiB3ZSBydW4gYQpMTSBvbiB0aGUgZGF0YXNldC4gRm9yIGJsdWUgYW5kIGdyZWVuIHdlIHNlZSBzYW1lIHJlbGF0aW9uc2hpcC4gQXMgc2hvd24KaGVyZSBhIGhpZ2hlciBmbGV4aWJpbGl0eSBtb2RlbCBpcyBub3QgbGF3YXlzIHRoZSBvcHRpbWFsIGNob2ljZS4gKipJbgp0aGUgZXhhbXBsZSB3ZSB3b3VsZCBnbyBmb3IgdGhlIGJsdWUgTVNFIHRvIGF2b2lkIG92ZXJmaXR0aW5nKiouCgpXZSBhbHdheXMgYWltIGZvciBsb3dlc3QgdGVzdCBlcnJvci4KCiMjIExlY3R1cmUgY29uY2x1c2lvbnMKCkluY3JlYXNlIGluIG1ldGhvZCBmbGV4aWJpbGl0eSAobW9yZSBhZHZhbmNlZCBtZXRob2RzLCBOTiksIHdlIGNhbgpyZWR1Y2UgdGhlIHByZWRpY3Rpb24gZXJyb3IgKGJpYXMpLiBJbmNyZWFzaW5nIGZsZXhpYmlsaXR5IGRvZXMgaG93ZXZlcgpoYXZlIGRpbWluaXNoaW5nIHJldHVybnMgYW5kIHdpbGwgZXZlbnR1YWxseSBpbmNyZWFzZSBvdXIgdmFyaWFuY2UKZnVydGhlciB0aGFuIHJlZHVjaW5nIG91ciBiaWFzLgoKTWFjaGluZSBsZWFybmluZyBoYXMgdG8gb25lLXNpemUtZml0cy1hbGwgbW9kZWwsIHdlIG11c3QgdXRpbGl6ZSBhbGwKdG9vbHMgYW5kIG1vZGVscyBhdmFpbGFibGUgdG8gdXMgdG8gdHJlYXQgZWFjaCBkYXRhc2V0IGluZGVwZW5kZW50bHkuCgpgYGB7cn0KCmBgYAoKIyMgTGVjdHVyZSA0IGNvZGluZyDwn6STCgpTYW1wbGUgZm9ybXVsYSBpbnRlcmZhY2VzCgpgYGB7cn0KCmFtZXMgPC0gQW1lc0hvdXNpbmc6Om1ha2VfYW1lcygpCiMgU2FsZSBwcmljZSBhcyBmdW5jdGlvbiBvZiBuZWlnaGJvcmhvb2QgYW5kIHllYXIgb2xkCgpsbV9sbSA8LSBsbShTYWxlX1ByaWNlIH5OZWlnaGJvcmhvb2QgKyBZZWFyX1NvbGQsIGRhdGEgPSBhbWVzKQoKI2xtIGlzIHVzZWQgdG8gZml0IGxpbmVhciBtb2RlbHMKCmxtX2dsbSA8LSBnbG0oU2FsZV9QcmljZSB+TmVpZ2hib3Job29kICsgWWVhcl9Tb2xkLCBkYXRhID0gYW1lcywgZmFtaWx5ID0gZ2F1c3NpYW4pCgojZ2xtIGlzIHVzZWQgdG8gZml0IGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMKCmxtX2NhcmV0IDwtIHRyYWluKFNhbGVfUHJpY2Ugfk5laWdoYm9yaG9vZCArIFllYXJfU29sZCwgZGF0YSA9IGFtZXMsIG1ldGhvZCA9ICJsbSIpCmxtX2NhcmV0CgojdHJhaW4gdXNlZCBhcyBwYXJ0IG9mIHRoZSBDYXJldCBsaWJyYXJ5LiBEb2N1bWVudGF0aW9uIGZvdW5kIGluIHRmZXN0aW1hdG9ycyBwYWNrYWdlIAoKCgpgYGAKCiMjIEluIGNsYXNzIHdpdGggQW5hIGZvbGxvdy1hbG9uZwoKIyBMZWN0dXJlIDMKCnJldmlldzoKCiRrbm4kLXJlZ3Jlc3Npb24KCmstbmVhcmVzdCBuZWlnaGJvdXIsICRrJCA9IHggLSBzaW1wbGUgTUwgYWxnbyAtIGJhc2VkIG9uIGNhbGN1bGF0aW5nCmRpc3RhbmNlcyBiZXR3ZWVuIG9ic2VydmF0aW9ucyAtIGRpc3RhbmNlIGUuZy4gZXVjbGlkZWFuIChldWNsaWRlYW4Kc29ydGluZyBhbGdvKSwgTUFOSEFUVEFOLCAkZWskIGluIGEgbXVsdGlkaW1lbnNpb25hbCBzcGFjZSBvZgokWCQocHJlZGljdG9ycykgLSBwcmVkaWN0IGJ5IGF2ZXJhZ2luZyBkZXBlbmRhbnQgdmFyaWFibGVzIGZvciB0aGUKY2xvc2VzdCBLLW5laWdoYm91cnMgLSBrIGlzIHR1bmVkICh3ZSBjaGFuZ2Ugb3J1IGsgaW5wdXQgYmFzZWQgb24Kd2hhdGV2ZXIpCgotICAgay1ubiBhbGdvIGlzIGxhenkgKHNsb3csIGhhcyB0byBzb3J0IGZvciBldmVyeSBrKSwKLSAgIHdlIFNRVyg/KSBhbiBpbXBsZW1lbnRhdGlvbiBLTk4tcmVnIHVzaW5nIEFNRVMgZGF0YQoKIyMgVG9kYXk6IEZlYXR1cmUgJiB0YXJnZXQgZW5naW5lZXJpbmcgKGFsbW9zdCBzeW5vbnltb3VzIHdpdGggZGF0YSBwcmVwcm9jZXNzaW5nKQoKLSAgIFJvbGUKLSAgIFBsYWNlIGluIHRoZSBwcm9jZXNzCi0gICBoYW5kcyBvbiAkZXgkIGluIFIKClByZS1wcm9jZXNzaW5nIHN0ZXA6CgpXaGljaCBpcyBvcHRpbWFsIGluIHRlcm1zIG9mIHJlc291cmNlcyh0aW1lKSA9IGFsdGVybmF0aXZlIDIgd2hpY2ggaWRlYWwKaW4gdGVybXMgb2YgYWNjdXJhY3k/ID0gYWx0ZXJuYXRpdmUgMwoKICAgIGlmIGRvbmUgYXQgc3RlcCAxIHdlIGF2ZXJhZ2UgdGhlIGRhdGFzZXQgaW1tZWRpYXRlbHkuIFdlIHdhbnQgYSBwdXJlIHVudG91Y2hlZCBkYXRhc2V0IHdpdGhvdXQgaW5mbHVlbmNlIGZybW8gb3VyIGFjdGlvbnMuCgogICAgaWYgZG9uZSBhdCBzdGVwIDIgd2Ugc3RhbmRhcmRpemUgdGhlIG1lYW4gZm9yIHRoZSB3aG9sZSBkYXRhc2V0LiBUaGUgc2FtZSBtZWFuIHdpbGwgYmUgaW1wbGVtZW50ZWQgaW4gYm90aCB0aGUgYW5hbHl6aXMgb2YgdGhlIG1vZGVsIGFuZCB0aGUgZXZhbHVhdGlvbi4gRG9pbmcgaXQgdGhpcyB3YXkgaXMgZ29vZCAKCiAgICBJZiBkb25lIGF0IHN0ZXAgMyB3ZSBjYW4gZG8gdGhlIGludGVncmF0aW9ucyBiZXR3ZWVuIGVhY2ggZm9sZCBvZiB0aGUgbW9kZWwuIFRoaXMgaW5jcmVhc2VzIHRoZSBhY2N1cmFjeSBiZXR3ZWVuIGVhY2ggZm9sZCBmb3IgdGhlIG5leHQgcGFyc2UuIFRoaXMgdGFrZXMgYSBsb2FkIG9mIHRpbWUgZHVlIHRvIG11bHRpcGxlIG9wdGltaXphdGlvbnMKCiAgICAtIGRhdGEgbGVha2FnZTogaW4gc29tZSB3YXkgdGhlIHByb2Nlc3Mgb2YgbW9kZWxsaW5nIHdlIGFmZmVjdCB0aGUgdW5zZWVuIG9ic2VydmF0aW9ucyB3aXRoIGluZm9ybWF0aW9uIGZyb20gdGhlIHRyYWluaW5nIG9ic2VydmF0aW9ucy4gQ2FuIHNrZXcgZGF0YXNldCBhbmQgaW1wYWN0IHlvdXIgZXJyb3IuCgpXaGVuIFkgaXMgc2tld2VkIHdlIHdhbnQgdG8gdHJhbnNmb3JtIGl0LiBDb21tb24gdHJhbnNmb3JtYXRpb24gbWV0aG9kcwppbmNsdWRlIC0gTG9nIHRyYW5zZm9ybWF0aW9uIChtb3N0IGNvbW1vbiwgZG9lc24ndCBmdW5jdGlvbiBvbgowLXZhbHVlcykgLSBCb3ggQ294IHRyYW5zZm9ybWF0aW9uIChtb3N0IGZsZXhpYmxlLCBjYW4gYmUgYXBwbGllZCB0bwpwb3NpdGl2ZSBhbmQgMC12YWx1ZXMpIC0gWWVvLUpvaG5zb24gdHJhbnNtZm9ybWF0aW9uIChIYW5kbGVzIG5lZ2F0aXZlCnZhbHVlcykKClByZWRpY3Rpb25zIGZvbGxvdyB0cmFuc2Zvcm1hdGlvbiBzY2FsZSAoaWYgd2UgbG9nIHRyYW5zZm9ybSByZXN1bHQgd2lsbAphbHNvIGJlIGxvZykKCiMjIyBtaXNzaW5nIHZhbHVlcyAxIAoKLSAgIG1pc3NpbmcgdmFsdWVzIGNhbiBiZSBpbmZvcm1hdGl2ZSAoS3VobiBhbmQgSm9obnNvbiAyMDEzKQoKICAgIGRlcGVuZCBvbiBkYXRhIGNvbGVsY3Rpb24gYW5kIGRlc2VydmUgb3duIGNhdGVnb3J5CgotICAgbWlzc2luZyBhdCByYW5kb20gKExpdHRsZSBhbmQgUnViaW4gMjAxNCkgQ2FuIGJlIGRlbGV0ZWQgb3IgaW1wdXRlZAoKICAgIGluIFIsIHR5cGljYWxseSBOQSBvciBOYU4sIGFueSBhbnkgY2hhcmFjdGVyIGNhbiBkZWZpbmUgYSBtaXNpbmcsCiAgICBibGFuayBzcGFjZSBldGMuCgotICAgSWYgeW91IHdpc2ggdG8gZGl2aWRlIHZhcmlhYmxlcyBhbmQgeW91IGRpdmlkZSBieSB6ZXJvIHlvdSBjYW4gZ2V0IGEKICAgIE5BIHZhcmlhYmxlIGFzIHlvdSBjYW5ub3QgZGl2aWRlIGJ5IHplcm8uIEJlIGF3YXJlIG9mIHJhbmRvbSBtaXNzaW5nCiAgICBkYXRhIG9jY3VyaW5nCgojIyMgTWlzc2luZyB2YWx1ZXMgMgoKLSAgIFVzZSBwbG90cyB0byBpZGVudGlmeSBtaXNpbmcgZGF0YSBidW5jaC4gdXNlZnVsIGZvciBjb2xvciBjb2RpbmcKCiMjIyBpbXB1dGF0aW9uIDEKCkltcHV0YXRpb24gaXMgdXNlZCBmb3IgcmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIHdpdGggb3VyIGJlc3QgZ3Vlc3Nlcy4KCk1ldGhvZHM6CgotICAgRXN0aW1hdGVkIHN0YXRpc3RpY3M6IE1lYW4sIE1lZGlhbiwgbW9kZSAoIGZvciBjYXRlZ29yaWNhbCkKCi0gICBLLW5lYXJlc3QgbmVpZ2hib3VycywgVHJlZS1iYXNlZCBtb2RlbHMgKHVzZSBrbm4gdG8gaW1wdXRlIG1pc3NpbmcKICAgIHZhbHVlLCB1c2UgdGhlIGF2ZXJhZ2UgdmFsdWUgb2YgdGhlIHggbmVhcmVzdCBuZWlnaGJvdXJzIHRvCiAgICBlc3RpbWF0ZSkKCioqQXMgSW1wdXRhdGlvbiBpcyBkb25lIHdpdGhpbiByZS1zYW1wbGluZyBpdCBpbmNyZWFzZXMgcHJvY2Vzc2luZwpkZW1hbmRzLioqCgojIyMgaW1wdXRhdGlvbiAyCgpgYGB7cn0KaW5jbHVkZV9ncmFwaGljcygiSW1wdXRhdGlvbiAyLnBuZyIpCmBgYAoKTWVhbiBpbXB1dGF0aW9uIGlzIGEgY29uc3RhbnQgYXZlcmFnZSwgZGF0YSBkb2Vzbid0IGdldCBhIG5pY2UgZml0CgpLTk4gaW1wdXRhdGlvbiBlc3RpbWF0ZXMgdmFsdWVzIGJldHRlciB1c2luZyBvdGhlciBjbG9zZSB2YWx1ZXMKCkJhZ2dlZCB0cmVlcyAoZGVjaXNpb24tdHJlZSBtb2RlbD8pIEFsc28gZ2V0cyBhIGdyZWF0IGZpdC4gUHJlc2VydmVzCnJlbGF0aW9uc2hpcC4KCioqRnJvbSBoZXJlIG9uIG91dCBjb2RlYmxvY2tzIGluIGxlY3R1cmUgMyBhcmUgcmVsYXRlZCB0byB0aGUKRmVhdHVyZV9lbmdpbmVlcmluZyBSIGZpbGUgZnJvbSB0aGUgbGVjdHVyZS4qKgoKIyMjIEZlYXR1cmUgZmlsdGVyaW5nCgpXaHk/CgotICAgTW9kZWwgd2l0aCBtYW55IGZlYXR1cmVzCgogICAgLSAgIEhhcmQgdG8gaW50ZXJwcmV0CgogICAgICAgIC0gICBDb3N0bHkgdG8gY29tcHV0ZSAodGltZSwgcHJvY2Vzc2luZyBwb3dlcikKCi0gICBTb21lIG1vZGVscyBhcmUgaW5mbGV4aWJsZSB0byBub24tZm9ybWF0aXZlIHByZWRpY290cnMgKGUuZy4sKSB0aGUKICAgIGxhc3NvIG1vZGVsIGFuZCB0cmVlLWJhc2VkIG1vZGVsKS4gT3RoZXJzIGFyZSBuZWdhdGl2ZWx5IGFmZmVjdGVkLgoKYGBge3J9CgppbmNsdWRlX2dyYXBoaWNzKCJSTVNFIC0gd2l0aCBub24gZm9ybS5wbmciKQpgYGAKCkhlcmUgd2UgYXJlIHRyeWluZyBvdCB1bmRlcnRhZG4gaG93IG11Y2ggb2YgdGhlIGVycm9yIGlzIGltcGFjdGVkIGJ5IG91cgpub24gaW5mb3JtYXRpdmUgZXJyb3JzLiBPdXIgbm9uIGluZm9ybWF0aXZlIGVycm9lcyBhcmUgb3VyIFgncyB3aGVyZSB3ZQpoYXZlIG1hbnkgbWlzc2luZyB2YWx1ZXMgb3IgbWFueSBpbnZhcmlhYmxlIHZhcmlhYmxlcy4gSWYgdGhlIGRhdGEgb2YKdGhlIHZhcmlhYmxlcyBhcmUgbWlzc2luZyBvciBpbnZhcmlhYmxlIGl0IHdpbGwgaGF2ZSBubyBpbXBhY3Qgb24gb3VyCkRWLgoKLSAgIHJlbW92ZSB6ZXJvIG9yIG5lYXItemVybyB2YXJpYW5jZSBmZWF0dXJlcwoKQXMgc2VlbiBvbiB0aGUgZ3JhcGgsIG1vcmUgemVybyBvciBuZWFyLXplcm8gdmFyaWFuY2UgZmVhdHVyZXMgaW5jcmVhc2VzClJNU0UKCiMgVHV0b3JpYWwgMgoKUHJvYmxlbSAxOiBQcm9ncmFtbWluZyBUaGUgcHVycG9zZSBvZiB0aGlzIHByb2JsZW0gaXMgdG8gZ2V0IGNvbWZvcnRhYmxlCndpdGggUiBhbmQgaXRzIGZhY2lsaXRpZXMuIFdlIHNoYWxsIHNwZW5kbW9zdCBvZiB0aGUgdGltZSBkb2luZyBzb21lCmJhc2ljIGNvbXB1dGF0aW9ucy4gSWYgeW91IGFyZSBhIGdvb2QgcHJvZ3JhbW1lciB5b3Ugd2lsbCBmaW5pc2h0aGVzZQpjb21wdXRhdGlvbnMgcXVpY2tseS4gRmlyc3Qgc3RhcnQgYnkgb3BlbmluZyBSLCBjcmVhdGUgYSBuZXcgc2NyaXB0IGFuZApzYXZlIGl0IHRvIHlvdXIgaGFyZCBkcml2ZSB3aXRoIHRoZSBuYW1lOiAiRXhlcmNpc2UxLlIiLgoKUGFydCAxCgpgYGB7cn0KCnYxIDwtIGMoMSwyLDIsMSkKdjIgPC0gYygyLDMsMywyKQoKdjErdjIKdjEtdjIKdjEqdjIKdjMgPC0gYyh2MSx2MikKCmBgYAoKUGFydCAyCgpgYGB7cn0KCiMxIAptMSA8LSBjKDEsNiwzLDIsNCw2KQptQSA8LSBtYXRyaXgobTEsbmNvbD0yKQptQQoKIzIKcHJpbnQobUFbMSxdKQpwcmludChtQVsyLF0pCnByaW50KG1BWzMsXSkKcHJpbnQobUFbLDFdKQpwcmludChtQVssMl0pCgpyb3dTdW1zKG1BKQoKCmFwcGx5KG1BLCAxLCBGVU49bWluKSAKYXBwbHkobUEsIDEsIEZVTj1tYXgpIAoKc29ydChtQVssMV0sZGVjcmVhc2luZyA9IEZBTFNFKQoKIzMKCm1EIDwtIG1hdHJpeCgxOjEsIG5jb2w9IDQsIG5yb3c9NCkKCm1EW2MoMSw2LDExLDE2KV0gPC0gMAptRAoKbUQgPC0gbWF0cml4KDEsIG5yb3c9NCwgbmNvbD00KQoKZGlhZyhtRCkgPC0gcmVwKDAsIG5yb3cobUQpKSAKbUQKCgptRSA8LSBtYXRyaXgoMToxNiwgbmNvbD00LG5yb3c9NCxieXJvdz1UUlVFKQptRQptRVstYygzLDUsNiw5LDE2KV0gPC0gMAoKbUUKbWkgPC0gZGlhZyh4PTEsIG5yb3c9NCwgbmNvbD00KQoKCiM0CgptRiA8LSAocmJpbmQobUQsbUUpKQptRgoKbUUrbUQKbUUqbUQKCm1FICUqJSBtRCAjbWF0cml4IHByb2R1Y3QKCgoKIzUgCnggPSAxCmNhbGNfeCA8LSB7CiAgICBpZih4IDw9IDApIHsKICAgICAgcHJpbnQoIi14XjMiKSAKICAgIH0gZWxzZSB7CiAgICAgIGlmKHggPiAxKSB7IAogICAgICAgIHByaW50KCJzcXJ0eCIpCiAgICAgICB9IGVsc2Uge3ByaW50KCJ4XjIiKQogICAgICAgfQogICAgfQp9CiAgICAgICAKY2FsY194CgojNiBidXN0ZWQgbcOlZGUKIyBoKHgsbik9MSAreCt4Mit4MyvCt8K3wrcreG494oiRbmk9MHhpCiMgdXNpbmcgcmVwbGljYXRlIGl0J3MgZWFzeSB0byBtYXRjaCB4IHRvIG4KCiNmdW5jIDwtIGZ1bmN0aW9uKGh4bikKICMgewojZm9yIChqIGluIDE6bikKI3sKIyAgeFtqXSA9IGpebgojfQojIHgKI30KCiNuID0gNgojeF8xID0gbgojeCA9IHJlcCh4XzEsbikKCiNmdW5jKGh4bikKCiM2CmZ1bmMgPC0gZnVuY3Rpb24oeCxuKQp7CiAgc3VtID0gMAogIAogIGZvciAoaiBpbiAwOm4pCiAgewogICAgc3VtID0gc3VtICsgeF5qCiAgfQogIHJldHVybihzdW0pCn0KCmZ1bmMoeD0xLCBuPTIpCgojIDJeMCArIDJeMSArIDJeMiArIDJeMwoKIzcgZnVjayB3aGlsZSBsb29wcwogCmZ1bmMyIDwtIGZ1bmN0aW9uKHgsbikKewogIHN1bSA9IDAKICBqID0gMCAKICB3aGlsZSAoaiA8PSBuKQogIHsKICAgIHN1bSA9IHN1bSArIHheagogICAgaiA9IGogKyAxCiAgfQogIHJldHVybihzdW0pCn0KCmZ1bmMyKHg9Mywgbj0zKQoKCmZ1bmMzIDwtIGZ1bmN0aW9uKHgsbikKewp4MSA8LSBjKDA6eCkKcHJpbnQoeDEpCnsKICBuMSA8LSAoMDpuKQogIHByaW50KG4xKQp9Cm54MSA8LSB4MV5uMQpueDEKcHJpbnQoc3VtKG54MSkpCn0KCmZ1bmMzKHg9MywgbiA9IDMpCgoKICAjOAoKIyBBIHJvb20gY29udGFpbnMgMTAwIHRvZ2dsZSBzd2l0Y2hlcywgb3JpZ2luYWxseSBhbGwgdHVybmVkIG9mZi4gIDEwMCBwZW9wbGUgZW50ZXIgdGhlIHJvb21pbiB0dXJuLiAgVGhlIGZpcnN0IG9uZSB0b2dnbGVzIGV2ZXJ5IHN3aXRjaCwgdGhlIHNlY29uZCBvbmUgdG9nZ2xlcyBldmVyeSBzZWNvbmQgc3dpdGNoLCB0aGVvbmUgdGhpcmQgZXZlcnkgdGhpcmQgc3dpdGNoLCBhbmQgc28gb24gdW50aWwgdGhlIGxhc3QgcGVyc29uLCB3aG8gdG9nZ2xlcyB0aGUgbGFzdCBzd2l0Y2ggb25seS4gIEF0IHRoZSBlbmQgb2YgdGhpcyBwcm9jZXNzLCB3aGljaCBzd2l0Y2hlcyBhcmUgdHVybmVkIG9uP05vdGU6VGhpcyByZXF1aXJlcyBhbGl0dGxlIHRoaW5raW5nLiBEb27igJl0IGdpdmUgdXAhCiAgCiNyZXN0IHN0YXRlID0gMTAwIG9mZgojZmlyc3QgcGFzcyA9IDEwMCBvbgojc2Vjb25kIHBhc3MgPSA1MCBvbiwgYygxOjEwMCwyKSBpcyBvbgojdGhpcmQgcGFzcyA9IAoKCmBgYAoKIyM4IHBlcnNvbiAkaSQgd2lsbCBmbGlwIGxpZ2h0cyBidWxicyB0aGF0IGFyZSBtdWx0aXBsZXMgb2YgaQoKJCQgaSBcaW4gKHsxLDIsMyDigKYgMTAwfSkgJCQKCnRoZXJlZm9yOwoKJCQgaSxqIFxpbiAoezEsMiwzIOKApiAxMDB9KSAkJAoKTGlnaHRidWxiIDUgd2lsbCBiZSBmbGlwcGVkIGJ5IHBlb3BsZSB0aGF0IGFyZSBhIGZhY3RvciBvZiA1ICg1IGlzIHByaW1lCm51bWJlcikKCiQkIGksNSBcaW4gKHsxLDV9KSAkJCBsaWdodGJ1bGIgMTAgd2lsbCBiZSBmbGlwcGVkIGJ5IHBlb3BsZSB0aGF0IGEKZmFjdG9yIG9mIDEwICQkIGksNSBcaW4gKHsxLDIsNSwxMH0pJCQKCkxpZ2h0IGJ1bGIgNDAgd2lsbCBiZSBmbGlwcGVkIGJ5IHBlb3BsZSB0aGF0IGFyZSBhIGZhY3RvciBvZiA0MAokJCBpLDUgXGluICgxLCAyLCA0LCA1LCA4LCAxMCwgMjAsIDQwKSAkJAoKRXhhbXBsZTogTGlnaHQgYnVsYiAyNSB3aWxsIGJlIGZsaXBwZWQgYnkgZmFjdG9yaWFscyBvZiAyNQokJCBpLDI1IFxpbiAoezEsNSwyNX0pICQkIFN0YXRlIDEsIGFsbCBsaWdodGJ1bGJzIGFyZSBvZmYKCnN0YXRlIDIsIGFsbCBsaWdodGJsdWJzIGFyZSBvbiAocGVyc29uIDEpCgpzdGF0ZSA1LCBldmVyeSBmaWZ0aCBsaWdodCBidWxiIGlzIHN3YXBwZWQuIDI1IGlzIG5vdyBvZmYgKHBlcnNvbiA1KQoKc3RhdGUgMjUsIGV2ZXJ5IHR3ZW50aHlmaWZ0aCBsaWdodCBibHViIGlzIHN3YXBwZWQuIDI1IGlzIG5vdyBvbiAocGVyc29uCjI1KQoKT25seSB0aGUgZmFjdG9yaWFscyBvZiB4IGltcGFjdHMgc3RhdHVzLgoKS25vd2luZyB0aGlzIHdlIGRlZmluZSBjYW4gbWFrZSB0aGUgZnVuY3Rpb24KCmBgYHtyfQoKbGlnaHRiIDwtIGZ1bmN0aW9uKG5idWxiLCBzd2l0Y2ggPSBuYnVsYikgewogICAgYnVsYiA8LSBsb2dpY2FsKG5idWxiKQogICAgZm9yIChpaSBpbiBzZXEoc3dpdGNoKSkgewogICAgICBtYWtlMSA8LSBzZXEoaWksIG5idWxiLCBpaSkKICAgICAgYnVsYlttYWtlMV0gPC0gIWJ1bGJbbWFrZTFdCiAgICB9CiAgICB3aGljaChidWxiKQp9CgoKbGlnaHRiKDEwMCkKYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIHJlcCBmdW5jdGlvbiBhcyBjb21tZW50ZWQgYWJvdmUgZm9yICM2IGZvciBuaWNlcgpzeW50YXgKCkxpbmUgZm9yIExpbmU6Cgp4ID0gcmVwKDEsMTAwKSwgZGVmaW5lIHggYXMgMSByZXBlYXRlZCAxMDAgdGltZXMgKDEwMCBsaWdodGJ1bGJzKQoKZGVmaW5lIHRoZSBmb3IgbWV0cmljLCBoZXJlIGZvciBpIGluIHRoZSByYW5nZSBvZiB4LCAxOjEwMC0xLiBXZSB1c2UgLTEKaGVyZSB0byBnYWluIDAgYW5kIDEgdmFsdWVzLCB3aGljaCB0aGUgeG9yIGNvbW1hbmQgdGhlbiB0cmFuc2xhdGVzIHRvCnRydWUgb3IgZmFsc2UuIFRyeSBwcmludGluZyBhZnRlciBlYWNoIGxpbmUgdG8gc2VlIGhvdyB4IGJlaGF2ZXMuCgpmb3IgYSByZXAgc2VxdWVuY2UsIGxlbmd0aC5vdXQgPSBub24tbmVnYXRpdmUgaW50ZWdlci4gVGhlIGRlc2lyZWQKbGVuZ3RoIG9mIHRoZSBvdXRwdXQgdmVjdG9yLiBPdGhlciBpbnB1dHMgd2lsbCBiZSBjb2VyY2VkIHRvIGEgZG91YmxlCnZlY3RvciBhbmQgdGhlIGZpcnN0IGVsZW1lbnQgdGFrZW4uIElnbm9yZWQgaWYgTkEgb3IgaW52YWxpZC4uCgpUaGVyZWZvciBsZW5ndGggb3V0IGRpY3RhdGVzIHRoZSBhbW91bnQgb2YgdGltZXMgd2UgcnVuIHRoZSBzZXF1ZW5jZS4KCnRyeSBwbGF5aW5nIGFyb3VuZCB3aXRoIHdpdGggdGhlIGZ1bmN0aW9uLgoKYGBge3J9Cgp4IDwtIHJlcCgxLDEwMCkKI3ByaW50KHgpCgpmb3IgKGkgaW4gKDE6MTAwLTEpKSB7CiAgeCA8LSB4b3IoeCwgcmVwKGMocmVwKDAsaSksMSksIGxlbmd0aC5vdXQ9MTAwKSkKICAjcHJpbnQoeCkKfQp4CndoaWNoKCF4KQoKYGBgCgojIFByb2JsZW0gMiwgTGluZWFyIHJlZ3Jlc3Npb24KCiMjIyBwcm9ibGVtIDIuMQoKQ29uc2lkZXIgdGhlIHN0YW5kYXJkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsICQkWT1YzrIrzrUkJCB3aGVyZSAkWSQgaXMKYW4tZGltZW5zaW9uYWwgb3V0Y29tZSB2ZWN0b3IsICRYJCBhbiAkbsOXcCQgZGltZW5zaW9uYWwgcmVncmVzc29yCm1hdHJpeCwgJM6yJCBhICRwJC1kaW1lbnNpb25hbCB2ZWN0b3Igb2YgY29lZmZpY2llbnRzLCBhbmQgJM61JCBhbgokbiQtZGltZW5zaW9uYWwgZXJyb3IgdGVybSB2ZWN0b3IuIFRoZSBPcmRpbmFyeUxlYXN0IFNxdWFyZXMgZXN0aW1hdG9yCmZvciAkzrIkIGlzIGdpdmVuIGJ5CgokJGhhdHvOsn09IChY4oCyWCniiJIxWOKAslkkJAoKd2hlcmUgJOKIkjEkIGRlbm90ZSB0aGUgbWF0cml4IGludmVyc2UgYW5k4oCyaXMgdGhlIG1hdHJpeCB0cmFuc3Bvc2UuIFRoZQpwcmVkaWN0aW9ucyBmcm9tIGEgbGluZWFycmVncmVzc2lvbiBtb2RlbCBhcmUgZ2l2ZW4gYnkKCiQkXGhhdHtZfT1YXGhhdHvOsn0kJAoKdGhlIHJlc2lkdWFscyBhcmUgZ2l2ZW4gYnkKCiQkZT1Z4oiSXGhhdHtZfSQkCgpUaGUgZXJyb3IgdmFyaWFuY2UgaXMgZXN0aW1hdGVkIGFzCgokJCBcaGF0e8+DfV4yPSBcZnJhY3sxfXtuLXB9IFxzdW1fe2k9MX1ee259ZV9pXjIkJCB3aGVyZSAkZWkkIGRlbm90ZSB0aGUKJGktdGgkIGVudHJ5IG9mICRlJC4gVGhlIHZhcmlhbmNlIG9mICRcaGF0e86yfSQgdW5kZXIgZnVsbCBpZGVhbApjb25kaXRpb25zIGNhbiBiZSBlc3RpbWF0ZWQgYXMgJCRcaGF0e1Z9W1xoYXR7zrJ9XSA9IFxoYXR7z4N9XjIoWOKAslgpXi0xICQkCldyaXRlIGEgZnVuY3Rpb25mT0xTKCl0aGF0IGVzdGltYXRlcyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGFuZApwcm92aWRlcyBlc3RpbWF0aW9uIHJlc3VsdHMuIEl0IHNob3VsZCBoYXZlIHRoZSBmb2xsb3dpbmcgaW5wdXRzOgoKLSAgIEFuIGRhdGEgZnJhbWUgY29udGFpbmluZyBZYW5kIFgKCi0gICBUaGUgbGFiZWwgb3IgY29sdW1uIGVudHJ5IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGRlcGVuZGVudCB2YXJpYWJsZVkKCi0gICBUaGUgbGFiZWxzIG9yIGNvbHVtbiBlbnRyaWVzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHJlZ3Jlc3NvcnNYCgogICAgSXQgc2hvdWxkIHJldHVybiBhIGxpc3Qgb2JqZWN0IGNvbnRhaW5pbmc6CgotICAgVGhlIG51bWVyaWNhbCB2ZWN0b3Igb2YgY29lZmZpY2llbnQgZXN0aW1hdGVzy4bOsgoKLSAgIFRoZSBudW1lcmljYWwgdmVjdG9yIG9mIHByZWRpY3Rpb25zy4ZZCgotICAgVGhlIGVzdGltYXRlZCBlcnJvciB2YXJpYW5jZSDLhs+DMgoKLSAgIFRoZSBzdGFuZGFyZCBlcnJvcnMgZm9yy4bOsihIaW50OlRoZXNlIGFyZSB0aGUgc3F1YXJlLXJvb3RzIG9mIHRoZQogICAgZGlhZ29uYWwgZW50cmllcyBvZsuGVlvLhs6yXSkKCmBgYHtyfQoKPC0gYygxOjEwMCkKWSA8LSBjKDI6MTAwLDIpCmRmb2xzIDwtIGRhdGEuZnJhbWUoWCxZKQpoZWFkKGRmb2xzKQoKZk9MUyA8LSBmdW5jdGlvbigpIHsKICAKICAKICAKfQpwcmludCh4KQpgYGAK